package main

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"sync"
	"time"
)

func main() {
	httpCallByGoroutine()
}

func httpCallByGoroutine() {
	type respData struct {
		resp *http.Response
		err  error
	}
	respChan := make(chan *respData, 1)                                       // 创建一个传输响应报文的通道
	ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second) // 创建一个超时可取消的上下文
	waitGroup := &sync.WaitGroup{}                                            // 获取阻塞对象
	waitGroup.Add(1)                                                          // 添加任务
	go func() {                                                               // 创建一个发起请求的协程任务
		urlStr := "http://127.0.0.1:8888/index"
		resp, err := http.Get(urlStr) // 发起http请求
		res := &respData{
			resp, err,
		}
		respChan <- res // 将结果写入通道中
	}()
	go func() { // 创建一个依赖其他下游返回的协程任务
		defer waitGroup.Done() // 阻塞任务减一
		select {
		case res := <-respChan: // 等待下游接口返回
			if res.err != nil {
				log.Fatal(res.err)
			}
			readAll, err := ioutil.ReadAll(res.resp.Body)
			if err != nil && err != io.EOF {
				log.Fatal(res.err)
			}
			fmt.Println(string(readAll))
			// 或我知一天必找着爱
			return
		case <-ctx.Done(): // 等待超时释放资源，终止当前协程任务，避免内存泄露
			log.Fatal(ctx.Err())
			// [FATAL]2022/12/05 17:37:30.846522 /Users/dxm/Desktop/web/go/src/go-study/go_http_client/main.go:52: context deadline exceeded
		}
	}()
	waitGroup.Wait() // 阻塞等待任务执行完成
	cancelFunc()     // 手动取消释放资源（即在超时之前协程任务已经完成，所以需要手动取消释放资源）
	log.Println(ctx.Err())
	// [FATAL]2022/12/05 17:46:18.734841 /Users/dxm/Desktop/web/go/src/go-study/go_http_client/main.go:58: context canceled
}

func init() {
	log.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile)
	log.SetPrefix("[FATAL]")
}

func httpConfigClient() {
	urlStr := "http://127.0.0.1:8888/index"

	client := &http.Client{
		Timeout: 3 * time.Second, // 超时时间
	}
	resp, err := client.Get(urlStr)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	respData, err := ioutil.ReadAll(resp.Body)
	if err != io.EOF && err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(respData))

	// 使用 GET、POST 之外的方法发起请求
	request, err := http.NewRequest("PUT", urlStr, nil)
	if err != nil {
		log.Fatal(err)
	}
	response, err := client.Do(request)
	if err != nil {
		log.Fatal(err)
	}
	defer response.Body.Close()
	respData, err = ioutil.ReadAll(response.Body)
	if err != io.EOF && err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(respData))

	// 携带 Header 请求
	newRequest, err := http.NewRequest("GET", urlStr, nil)
	if err != nil {
		log.Fatal(err)
	}
	newRequest.Header.Add("User-ID", "春风秋雨")
	response, err = client.Do(newRequest)
	if err != nil {
		log.Fatal(err)
	}
	defer response.Body.Close()
	respData, err = ioutil.ReadAll(response.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(respData))
}

func baseHttpCall() {
	urlStr := "http://127.0.0.1:8888/index"

	// GET 请求
	resp, err := http.Get(urlStr)
	if err != nil {
		log.Fatalf("call www.baidu.com failed, err:%+v", err)
	}
	defer resp.Body.Close()

	reqData, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatalf("read resp.body failed, err:%+v", err)
	}
	fmt.Println(string(reqData))

	// POST 请求，并携带 JSON 数据的请求
	data := map[string]string{
		"name": "张三",
	}
	dataJson, err := json.Marshal(data)
	if err != nil {
		log.Fatal(err)
	}
	reader := bytes.NewReader(dataJson)

	post, err := http.Post(urlStr, "application/json;charset=utf-8", reader)
	defer post.Body.Close()
	if err != nil {
		log.Fatal(err)
	}
	var content []byte
	tmp := make([]byte, 1024)
	for true {
		n, err := post.Body.Read(tmp)
		if n > 0 {
			content = append(content, tmp[:n]...)
		}
		if err == io.EOF {
			break
		}
		if err != nil {
			fmt.Println(err)
		}
	}
	fmt.Println(string(content))

	// POST 表单请求
	formData := url.Values{
		"name": {"向死而生"},
	}
	response, err := http.PostForm(urlStr, formData)
	if err != nil {
		log.Fatal(err)
	}
	defer response.Body.Close()
	respData, err := ioutil.ReadAll(response.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(respData))

}
